package dbx

import (
	"fmt"
	"net/url"
	"strings"
	"time"

	"github.com/cloudwego/kitex/pkg/klog"
	"go.mongodb.org/mongo-driver/v2/mongo/options"
)

type MongoDB struct {
	RsName   string   `json:"rs_name" toml:"rs_name" yaml:"rs_name"`
	UserName string   `json:"username" toml:"username" yaml:"username"`
	DBName   string   `json:"db_name" toml:"db_name" yaml:"db_name"`
	Password string   `json:"password" toml:"password" yaml:"password"`
	Addrs    []string `json:"addrs" toml:"addrs" yaml:"addrs"`

	AuthMechanism string `json:"auth_mechanism" toml:"auth_mechanism" yaml:"auth_mechanism"`
	Ssl           TLS    `json:"ssl,omitempty" toml:"ssl,omitempty" yaml:"ssl,omitempty"`
}

//Standalone  独立服务器
//Sharded Cluster  分片集群
//Replica Set  副本集

//mongodb://myDatabaseUser:D1fficultP%40ssw0rd@ mongodb0.example.com:27017/?authSource=admin
//mongodb://myDatabaseUser:D1fficultP%40ssw0rd@ mongos0.example.com:27017,mongos1.example.com:27017,mongos2.example.com:27017/?authSource=admin
//mongodb://myDatabaseUser:D1fficultP%40ssw0rd@ mongodb0.example.com:27017,mongodb1.example.com:27017,mongodb2.example.com:27017/?authSource=admin&replicaSet=myRepl

func (r MongoDB) dsnOptions(query url.Values) string {
	if query == nil {
		query = url.Values{}
	}

	if len(r.AuthMechanism) != 0 {
		query.Set("authMechanism", r.AuthMechanism)
	}

	if len(r.RsName) != 0 {
		query.Set("replicaSet", r.RsName)
	}

	if len(r.DBName) != 0 {
		query.Set("authSource", r.DBName)
	}

	if r.Ssl.IsEnable() {
		query.Set("ssl", "true")
	}

	escape, err := url.QueryUnescape(query.Encode())
	if err != nil {
		panic(err)
	}

	return escape
}

func (r MongoDB) DSN(query url.Values) string {
	builderUri := strings.Builder{}

	switch len(r.Addrs) {
	case 0:
		klog.Error("set no addrs")
	default:
		builderUri.WriteString(fmt.Sprintf("mongodb://%s:%s@%s/%s", r.UserName, r.Password, strings.Join(r.Addrs, ","), r.DBName))
	}

	encode := r.dsnOptions(query)
	if len(encode) != 0 {
		builderUri.WriteString("?")
		builderUri.WriteString(encode)
	}

	return builderUri.String()
}

func (r MongoDB) URL(query url.Values) url.URL {
	u := url.URL{
		Scheme:      "mongodb",
		Opaque:      "",
		User:        url.UserPassword(r.UserName, r.Password),
		Host:        strings.Join(r.Addrs, ","),
		Path:        r.DBName,
		RawPath:     "",
		OmitHost:    false,
		ForceQuery:  false,
		RawQuery:    "",
		Fragment:    "",
		RawFragment: "",
	}
	u.RawQuery = r.dsnOptions(query)

	return u
}

func (r MongoDB) Option(handles ...func(*options.ClientOptions)) *options.ClientOptions {
	credential := options.Credential{
		AuthMechanism:           r.AuthMechanism,
		AuthMechanismProperties: nil,
		AuthSource:              r.DBName,
		Username:                r.UserName,
		Password:                r.Password,
		PasswordSet:             false,
		OIDCMachineCallback:     nil,
		OIDCHumanCallback:       nil,
	}
	clientOpts := options.Client().SetHosts(r.Addrs).SetAuth(credential).SetTimeout(5 * time.Second)
	clientOpts.SetMaxPoolSize(100)

	if len(r.RsName) != 0 {
		clientOpts.SetReplicaSet(r.RsName)
	}

	clientOpts.SetBSONOptions(&options.BSONOptions{
		UseJSONStructTags: true,
	})

	if r.Ssl.IsEnable() {
		clientOpts.SetTLSConfig(r.Ssl.Tls())
	}

	for _, handle := range handles {
		handle(clientOpts)
	}

	return clientOpts
}
